/*
JETware Hands-free Extension provides additional features for Windows Mobile HFP support.
Copyright (C) 2004-2009 Jim Thatcher

This program is free software; you can redistribute it and/or modify it under the terms of the 
GNU General Public License as published by the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; 
if not, write to the Free Software Foundation, Inc., 
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Source Control header

$Workfile: MessagesMAPI.cpp $
$Revision: 15 $
$Date: 2/03/07 12:48p $
*/
// Include the proper version of TAPI
#include "stdafx.h"
#include <windows.h>
#include "BTHFExt.h"
#include <cemapi.h>
#include <objbase.h>
#include <initguid.h>
#include <mapidefs.h>
#include <mapicode.h>
#include <mapitags.h>
#include <mapix.h>
#include <mapiutil.h>
#define INITGUID
#include <mapiguid.h>


/*
class CAdviseSink : public IMAPIAdviseSink
{
	// Constructors and destructors
public :
	CAdviseSink  (DWORD dwMsgStore);
	virtual ~CAdviseSink();
	
	STDMETHODIMP			QueryInterface(REFIID riid, LPVOID * ppvObj);
	STDMETHODIMP_(ULONG)	AddRef();
	STDMETHODIMP_(ULONG)	Release();
	
	STDMETHODIMP_(ULONG)	OnNotify (ULONG cNotify, LPNOTIFICATION lpNotifications);

private :	
	LONG		m_cRef;
	DWORD		m_dwMsgStore;
};
*/

// #ifndef RELEASE_OBJ
#define RELEASE_OBJ(s)\
    if (s != NULL)\
    {                   \
        s->Release();   \
        s = NULL;       \
    }
// #endif //RELEASE_OBJ

static IMAPISession  *   pSession    =   NULL;
TCHAR * g_szMsgStore[2] = {NULL, NULL};
DWORD g_dwMsgMax[2] = {25,25};
BOOL g_bAllMsgs[2] = {FALSE,FALSE};
CRITICAL_SECTION csMsgList;
BOOL bProcessingNewMail = FALSE;

typedef struct tagMSGMAP
{
	WORD		Index;
	ULONG		IDLen;
	LPENTRYID	EntryID;
	tagMSGMAP *	Next;
} MSGMAP, * LPMSGMAP;

typedef struct tagMSGSTORE
{
	IMsgStore *	pStore;
	LPMSGMAP	MsgList;
//	BOOL		ReadFirstMsg;
	DWORD		MsgCount;
	WORD		NextIndex;
	BOOL		Notify;
	WORD		NotifyMode;
//	ULONG		AdviseID;
//	CAdviseSink * AdviseSink;
} MSGSTORE, * LPMSGSTORE;

// Always start at index 2 to work around BMW ULF bug
MSGSTORE pStores[2] = {{NULL,NULL,0,DUMMY_MSG_COUNT+1,FALSE,0},{NULL,NULL,0,DUMMY_MSG_COUNT+1,FALSE,0}};

LPMSGMAP FindMessage(WORD wIndex)
{
	LPMSGMAP pCurr = pStores[g_dwMSSelected].MsgList;
	while (pCurr != NULL)
	{
		if (wIndex == pCurr->Index)
			return pCurr;
		pCurr = pCurr->Next;
	}
	return NULL;
}

BOOL DeleteMessage(WORD wIndex)
{
	LPMSGMAP pCurr = pStores[g_dwMSSelected].MsgList;
	LPMSGMAP pDelete = NULL;
	if ((pCurr != NULL) && (pCurr->Index == wIndex)) // First message is the one we want
	{
		pDelete = pCurr;
		pStores[g_dwMSSelected].MsgList = pDelete->Next;
	}
	else
	{
		while (pCurr != NULL)
		{
			if (pCurr->Next != NULL) // find match in 2nd entry or later
			{
				if (pCurr->Next->Index == wIndex)
				{
					pDelete = pCurr->Next;
					pCurr->Next = pCurr->Next->Next;
					break;
				}
			}
			pCurr = pCurr->Next;
		}
	}
	if (pDelete != NULL)
	{
		LocalFree(pDelete->EntryID);
		LocalFree(pDelete);
		pStores[g_dwMSSelected].MsgCount--;
		return TRUE;
	}
	return FALSE;
}

void DeleteMsgLists()
{
	for (int i=0; i<=MESSAGES_SM; i++)
	{
		LPMSGMAP pCurr = pStores[i].MsgList;
		while (pCurr != NULL)
		{
			pStores[i].MsgList = pCurr->Next;
			LocalFree(pCurr->EntryID);
			LocalFree(pCurr);
			pCurr = pStores[i].MsgList;
		}
	}
}

/*
BOOL IsNewMsg(DWORD dwMS, ULONG ulLen, LPENTRYID eID)
{
	BOOL bRet = TRUE;
	LPMSGMAP pCurr = pStores[dwMS].MsgList;
	while (pCurr != NULL)
	{
		if (ulLen == pCurr->IDLen)
		{
			ULONG ulMatch = FALSE;
			if (SUCCEEDED(pSession->CompareEntryIDs(ulLen, eID, pCurr->IDLen, pCurr->EntryID, 0, &ulMatch)))
			{
				if (ulMatch == TRUE)
					return FALSE;
			}
		}
		pCurr = pCurr->Next;
	}
	return bRet;
}
*/

// AddMsg returns index of new messege in list, or 0 if message is already in list
WORD AddMsg(ULONG ulLen, LPENTRYID pEntry)
{
	LPMSGMAP pCurr = pStores[g_dwMSSelected].MsgList;
	LPMSGMAP pPrev = NULL;
	if (pCurr == NULL) // handle case where list is empty
	{
		pStores[g_dwMSSelected].MsgList = (LPMSGMAP) LocalAlloc(LPTR, sizeof(MSGMAP));
		pCurr = pStores[g_dwMSSelected].MsgList;
	}
	else
	{
		ULONG ulMatch = FALSE;
		while (pCurr->Next != NULL)
		{
			if (SUCCEEDED(pSession->CompareEntryIDs(ulLen, pEntry, pCurr->IDLen, pCurr->EntryID, 0, &ulMatch)))
			{
				if (ulMatch == TRUE)
					return 0;
			}
			pCurr = pCurr->Next;
		}
		// Above test does not check for match on last entry in list
		if (SUCCEEDED(pSession->CompareEntryIDs(ulLen, pEntry, pCurr->IDLen, pCurr->EntryID, 0, &ulMatch)))
		{
			if (ulMatch == TRUE)
				return 0;
		}
		pCurr->Next = (LPMSGMAP) LocalAlloc(LPTR, sizeof(MSGMAP));
		pPrev = pCurr;
		pCurr = pCurr->Next;
	}
	if (pCurr != NULL)
	{
		pCurr->IDLen = ulLen;
		pCurr->Index = pStores[g_dwMSSelected].NextIndex++;
		pCurr->EntryID = (LPENTRYID) LocalAlloc(LPTR, ulLen);
		if (pCurr->EntryID == NULL)
		{
			pPrev->Next = NULL;
			pStores[g_dwMSSelected].NextIndex--;
			LocalFree(pCurr);
			return 0;
		}
		memcpy(pCurr->EntryID, pEntry, ulLen);
		pCurr->Next = NULL;
		pStores[g_dwMSSelected].MsgCount++;
#ifdef DEBUG
		char szLog[64];
		sprintf(szLog, "AddMsg %i - Len=%i, ID=%8.8X", pCurr->Index, ulLen, *((DWORD *)pEntry+1));
		LogString(szLog, DEBUG_MESSAGES);
#endif
		return pCurr->Index;
	}
	else
	{
#ifdef DEBUG
		LogString("Memory allocation failed in AddMsg", DEBUG_MESSAGES);
#endif
		return 0;
	}
}

void CleanupMAPI()
{
	// RELEASE_OBJ(pfldrInbox);
	for (int i=0; i<=1; i++)
	{
		/*
		if ((pStores[i].pStore != NULL) && (pStores[i].AdviseID != 0))
			pStores[i].pStore->Unadvise(pStores[i].AdviseID);
		if (pStores[i].AdviseSink != NULL)
		{
			delete(pStores[i].AdviseSink);
			CoUninitialize();
		}
		pStores[i].AdviseSink = NULL;
		pStores[i].AdviseID = 0;
		*/
		pStores[i].Notify = FALSE;
		DeleteMsgLists(); // redundant, but harmless, to do this the second time
		pStores[i].MsgList = NULL;
		RELEASE_OBJ(pStores[i].pStore);
		pStores[i].MsgCount = 0;
		// pStores[i].ReadFirstMsg = FALSE;
		pStores[i].NextIndex = DUMMY_MSG_COUNT+1;
	}
	RELEASE_OBJ(pSession);
	bProcessingNewMail = FALSE;
}

BOOL InitializeMapiSession()
{
	BOOL bRet = FALSE;
	BOOL bFoundStore = FALSE;
	if (pSession == NULL)
	{
		HRESULT             hr = S_OK;

#ifdef DEBUG
		CHAR szLog[64];
#endif
		hr = MAPIInitialize(NULL);
#ifdef DEBUG
		sprintf(szLog, "MAPIInitialize returned 0x%8.8X", hr);
		LogString(szLog, DEBUG_MESSAGES);
#endif
		if (SUCCEEDED(hr))
		{
			// First logon to the store.
			hr = MAPILogonEx(NULL, NULL, NULL, NULL, (LPMAPISESSION *) &pSession);
#ifdef DEBUG
			sprintf(szLog, "MAPILogonEx returned 0x%8.8X", hr);
			LogString(szLog, DEBUG_MESSAGES);
#endif
			if (SUCCEEDED(hr))
			{
				bRet = TRUE;
			} // SUCCEEDED MAPILogonEx
		} // SUCCEEDED MAPIInitialize
		if (!bRet)
		{
			RELEASE_OBJ(pSession);
		}

#ifdef DEBUG
		sprintf(szLog, "At exit return %i", bRet);
		LogString(szLog, DEBUG_MESSAGES);
#endif
	}
	else
		bRet = TRUE;
	return bRet;
}

DWORD InitializeMapiStore()
{
	BOOL bRet = FALSE;
	BOOL bFoundStore = FALSE;
	if (pSession == NULL)
	{
		bRet = InitializeMapiSession();
	}
	if (pSession != NULL)
	{
		if (pStores[g_dwMSSelected].pStore != NULL)
		{
			return (pStores[g_dwMSSelected].MsgCount);
		}
		else
		{
			LPMAPIFOLDER        pfldrInbox  =   NULL;

			HRESULT             hr = S_OK;
			IMAPITable      *   pTable      =   NULL;
			IMAPITable		*   pContentsTable = NULL;
			SRowSet         *   psrs        =   NULL;
			ULONG               rgTags[]    =   { 1, PR_RESOURCE_FLAGS };
			ULONG               cValues     =   0;
			LPSRowSet			pRows		=	NULL;

			ULONG				ulInboxIDn	=	0;
			LPENTRYID			pInboxID	=	NULL;
#ifdef DEBUG
			CHAR szLog[64];
#endif

			SizedSPropTagArray(3, Columns) =
			{
				3, {PR_DISPLAY_NAME, PR_RESOURCE_FLAGS, PR_ENTRYID}
			};
			SizedSPropTagArray(3, sContents) = { 3, {PR_ENTRYID, PR_MESSAGE_DELIVERY_TIME, PR_MESSAGE_FLAGS} };

#ifdef DEBUG_MAPI
			strcpy(szLog, "Calling GetMsgStoresTable");
			LogString(szLog, DEBUG_MESSAGES);
#endif
			// Get the message stores table
			hr = pSession->GetMsgStoresTable(MAPI_UNICODE, &pTable);
#ifdef DEBUG_MAPI
			sprintf(szLog, "GetMsgStoresTable returned 0x%8.8X", hr);
			LogString(szLog, DEBUG_MESSAGES);
#endif
			if (hr == S_OK)
			{
				hr = pTable->SetColumns((LPSPropTagArray)&Columns, NULL);
#ifdef DEBUG_MAPI
				sprintf(szLog, "SetColumns returned 0x%8.8X", hr);
				LogString(szLog, DEBUG_MESSAGES);
#endif
				if (hr == S_OK)
				{
					while(pTable->QueryRows(1, 0, &psrs) == S_OK)
					{
#ifdef DEBUG_MAPI
						sprintf(szLog, "QueryRows returned %i rows", psrs->cRows);
						LogString(szLog, DEBUG_MESSAGES);
#endif
						if (1 != psrs->cRows)
							break;
						else
						{
							if ((psrs->aRow[0].cValues > 0)&&(PR_ENTRYID == psrs->aRow[0].lpProps[2].ulPropTag))
							{
								if (g_szMsgStore[g_dwMSSelected] != NULL)
								{
									if (lstrcmp(psrs->aRow[0].lpProps[0].Value.lpszW, g_szMsgStore[g_dwMSSelected]) == 0)
										bFoundStore = TRUE;
								}
								else
									bFoundStore = TRUE;
#ifdef DEBUG
								LPSTR lpszProfileName = psrs->aRow[0].lpProps[0].Value.lpszA;
								sprintf(szLog, "MAPI store \"%S\" match=%i", lpszProfileName, bFoundStore);
								LogString(szLog, DEBUG_MESSAGES);
#endif
								if (bFoundStore)
									break;
							} // ENTRYID was returned
						} // Rows = 1
						if (psrs != NULL)
							FreeProws(psrs);
						psrs = NULL;
					} // SUCCEEDED QueryRows
				} // SUCCEEDED SetColumns
				RELEASE_OBJ(pTable);
			} // SUCCEEDED GetMsgStoresTable
			if (bFoundStore)
			{
				// Open this message store
				hr = pSession->OpenMsgStore(NULL, 
					psrs->aRow[0].lpProps[2].Value.bin.cb, 
					(ENTRYID *)psrs->aRow[0].lpProps[2].Value.bin.lpb, 
					NULL, 0, &(pStores[g_dwMSSelected].pStore));
#ifdef DEBUG_MAPI
				sprintf(szLog, "OpenMsgStore returned 0x%8.8X", hr);
				LogString(szLog, DEBUG_MESSAGES);
#endif
				FreeProws(psrs);
				if (hr == S_OK)
				{
					// Now get the Inbox folder
					hr = pStores[g_dwMSSelected].pStore->GetReceiveFolder(NULL, 0, &ulInboxIDn, &pInboxID, NULL);
#ifdef DEBUG_MAPI
					sprintf(szLog, "GetReceiveFolder returned 0x%8.8X", hr);
					LogString(szLog, DEBUG_MESSAGES);
#endif
					if (hr == S_OK)
					{
						hr = pStores[g_dwMSSelected].pStore->OpenEntry(ulInboxIDn, pInboxID, NULL, 0, NULL, 
							reinterpret_cast <IUnknown **>(&pfldrInbox));
#ifdef DEBUG_MAPI
						sprintf(szLog, "OpenEntry returned 0x%8.8X", hr);
						LogString(szLog, DEBUG_MESSAGES);
#endif
						if (hr == S_OK)
						{
							if (pfldrInbox->GetContentsTable(0, &pContentsTable) == S_OK)
							{
								ULONG ulNumMsgs = 0;
#ifdef DEBUG_MAPI
								sprintf(szLog, "GetContentsTable succeeded");
								LogString(szLog, DEBUG_MESSAGES);
#endif
								if (pContentsTable->GetRowCount(0,&ulNumMsgs) == S_OK)
								{
#ifdef DEBUG
									sprintf(szLog, "GetRowCount found %i rows", ulNumMsgs);
									LogString(szLog, DEBUG_MESSAGES);
#endif
									if (pContentsTable->SetColumns((LPSPropTagArray)&sContents, NULL) == S_OK)
									{
										WORD nIndex = 1;
										LPSRowSet	pMsgRows	=	NULL;
#ifdef DEBUG_MAPI
										sprintf(szLog, "SetColumns succeeded");
										LogString(szLog, DEBUG_MESSAGES);
#endif
										SizedSSortOrderSet(1, pSort)={ 1,0,0, {PR_MESSAGE_DELIVERY_TIME, TABLE_SORT_DESCEND} };
										pContentsTable->SortTable((LPSSortOrderSet)&pSort, 0);
										// loop through QueryRows until we have found Max Messages for this store
										// EnterCriticalSection(&csMsgList);
										while (pContentsTable->QueryRows(10, 0, &pMsgRows) == S_OK)
										{
											if (pMsgRows->cRows <= 0)
												break;
#ifdef DEBUG
											sprintf(szLog, "QueryRows returned %i rows", pMsgRows->cRows);
											LogString(szLog, DEBUG_MESSAGES);
#endif
											bRet = TRUE;
											// ulNumMsgs = pMsgRows->cRows;
											for (UINT i = 0; i < pMsgRows->cRows; i++)
											{
												if (nIndex < g_dwMsgMax[g_dwMSSelected] && ((g_bAllMsgs[g_dwMSSelected]) || (!(pMsgRows->aRow[i].lpProps[2].Value.ul & MSGFLAG_READ))))
												{
													WORD wMsgIndex = AddMsg(pMsgRows->aRow[i].lpProps[0].Value.bin.cb, 
														(LPENTRYID)pMsgRows->aRow[i].lpProps[0].Value.bin.lpb);
													/*
													if (wMsgIndex > 0)
													{
													}
													*/
												}
												nIndex++;
											}
											FreeProws(pMsgRows);
											if (nIndex >= g_dwMsgMax[g_dwMSSelected])
												break;
										} // SUCCEEDED QueryRows
										// LeaveCriticalSection(&csMsgList);
									} // SUCCEEDED SetColumns
								} // SUCCEEDED GetRowCount
							} // SUCCEEDED GetContentsTable
						} // SUCCEEDED OpenEntry
						// now set up the new message notification registration
						MAPIFreeBuffer(pInboxID);
					} // SUCCEEDED GetReceiveFolder
				} // SUCCEEDED OpenMsgStore
			}
			if (!bRet)
			{
				RELEASE_OBJ(pStores[g_dwMSSelected].pStore);
			}
			RELEASE_OBJ(pfldrInbox);
			RELEASE_OBJ(pContentsTable);

#ifdef DEBUG_MAPI
			sprintf(szLog, "At exit return %i", bRet);
			LogString(szLog, DEBUG_MESSAGES);
#endif
		}
	}
	else
		bRet = TRUE;
	return pStores[g_dwMSSelected].MsgCount;
}

DWORD InitializeMAPI()
{
	BOOL bRet = InitializeMapiSession();
	if (bRet)
	{
		EnterCriticalSection(&csMsgList);
		bRet = InitializeMapiStore();
		LeaveCriticalSection(&csMsgList);
	}
	return(bRet);
}

/*
DWORD GetMapiMessageCount()
{
	InitializeMAPI();
	bReadFirstMessage = FALSE;
	return (DWORD) ulNumMsgs;
}
*/

void SetMapiNotify(DWORD dwMsgStore, BOOL bNotify, WORD wMode)
{
	EnterCriticalSection(&csMsgList);
	pStores[dwMsgStore].Notify = bNotify;
	pStores[dwMsgStore].NotifyMode = wMode;
	if (bNotify)
	{
		if (pStores[dwMsgStore].pStore == NULL)
			InitializeMAPI();
	}
	LeaveCriticalSection(&csMsgList);
}

BOOL GetMapiNotify()
{
	return (pStores[g_dwMSSelected].Notify);
}

WORD GetMapiNotifyMode()
{
	return (pStores[g_dwMSSelected].NotifyMode);
}

BOOL ReadMapiMessage(int nIndex)
{
	LPMSGMAP pMsg = NULL;
	BOOL bRet = FALSE;
	EnterCriticalSection(&csMsgList);
/*
	if (nIndex == 1)
		pStores[g_dwMSSelected].ReadFirstMsg = TRUE;
	if (!pStores[g_dwMSSelected].ReadFirstMsg)
		nIndex--;
*/
	// if this is the first (or second) message, initialize MAPI
	// Don't initialize on each message in case there is an problem with MAPI we won't keep up a futile effort
	if ((nIndex <= 2) && (pSession == NULL))
		InitializeMAPI();
	// now read the message
	pMsg = FindMessage(nIndex);
	if (pMsg != NULL)
	{
#ifdef DEBUG
		CHAR szLog[1024];
#endif
		HRESULT hr = S_FALSE;
		ULONG ulObjType;
		IMessage * pMessage = NULL;
		SizedSPropTagArray(5,rgTags) = {5, {PR_SUBJECT, PR_SENDER_NAME, PR_MESSAGE_DELIVERY_TIME, PR_MESSAGE_FLAGS, PR_MSG_STATUS}};

#ifdef DEBUG
		sprintf(szLog, "Calling OpenEntry for message %i", nIndex-1);
		LogString(szLog, DEBUG_MESSAGES);
#endif
		if (pStores[g_dwMSSelected].pStore != NULL)
		{
			hr = pStores[g_dwMSSelected].pStore->OpenEntry(pMsg->IDLen, (LPENTRYID)pMsg->EntryID,
				NULL, 0, &ulObjType, (LPUNKNOWN FAR *)&pMessage);
#ifdef DEBUG
			sprintf(szLog, "OpenEntry returned %8.8X", hr);
			LogString(szLog, DEBUG_MESSAGES);
#endif
		}
		else
		{
			hr = -1;
#ifdef DEBUG
			LogString("ERROR: pStore == NULL", DEBUG_MESSAGES);
#endif
		}
		if (hr == S_OK)
		{
			ULONG ulPropCount = 0;
			LPSPropValue pProps = NULL;
#ifdef DEBUG
			sprintf(szLog, "Calling GetProps");
			LogString(szLog, DEBUG_MESSAGES);
#endif
			if (pMessage != NULL)
			{
				hr = pMessage->GetProps((LPSPropTagArray) &rgTags, MAPI_UNICODE, &ulPropCount, &pProps);
			}
			else
			{
				hr = -1;
			}
			if ((HR_SUCCEEDED(hr)) && (pProps != NULL)) // second case should always be true if first is true
			{
				//TODO: Add support for reporting in MSG_FORMAT_TEXT mode
				SYSTEMTIME st;
				FILETIME lft;
				char szMessage[512]; // only use 160*2
				char szRawTime[16]; // to hold the raw time characters
				char szPduTime[16];
				char szRawMsg[1024]; // to hold message characters
				char szPduMsg[512];
				CHAR szTemp1[256];
				CHAR szTemp2[256];
				int nPduMsgLen;
				int nMaxPduLen = 160;
				int nPrefixLen = 16; // TODO: allow for real sender addr len
				int nMsgRead = 1; // TODO: allow MsgRead to be set in registry - 3=Sent, read (hides from BMW display)
				CHAR szUnknown[8] = {'U','n','k','n','o','w','n',0};
				CHAR szNone[8] = {'(','N','O','N','E',')',0,0}; 
				// LPTSTR pszFrom = NULL;
				// LPTSTR pszSubject = NULL;
#ifdef DEBUG
				sprintf(szLog, "Got %i props: %i %i %i %i %i", ulPropCount, pProps[0].ulPropTag, pProps[1].ulPropTag, pProps[2].ulPropTag, pProps[3].ulPropTag, pProps[4].ulPropTag);
				LogString(szLog, DEBUG_MESSAGES);
#endif
				memset(&lft, 0, sizeof(lft));
				if (pProps[2].ulPropTag != PT_ERROR)
					FileTimeToLocalFileTime(&(pProps[2].Value.ft), &lft);
				FileTimeToSystemTime(&lft, &st);
				sprintf(szRawTime, "%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
				// TODO: Allow nMaxPduLen to be set in registry
				if (LOWORD(pProps[1].ulPropTag) == PT_TSTRING && pProps[1].Value.lpszA != NULL)
				{
					// pszFrom = (LPTSTR) pProps[1].Value.lpszA;
					if (!WideCharToMultiByte(CP_OEMCP, 0, (LPTSTR) pProps[1].Value.lpszA, -1, szTemp1, sizeof(szTemp1), NULL, NULL))
						strcpy(szTemp1, szUnknown);
				}
				else
					strcpy(szTemp1, szUnknown);
				if (LOWORD(pProps[0].ulPropTag) == PT_TSTRING && pProps[0].Value.lpszA != NULL)
				{
					// pszSubject = (LPTSTR) pProps[0].Value.lpszA;
					if (!WideCharToMultiByte(CP_OEMCP, 0, (LPTSTR) pProps[0].Value.lpszA, -1, szTemp2, sizeof(szTemp2), NULL, NULL))
						strcpy(szTemp2, "(NONE)");
				}
				else
					strcpy(szTemp2, "(None)");
				// TODO: Use None for empty subject
				sprintf(szRawMsg, /*"From: */"%s: %s", szTemp1, szTemp2);
				nPduMsgLen = strlen(szRawMsg);
#ifdef DEBUG
				LogString(szRawMsg, DEBUG_MESSAGES);
#endif

				if (nPduMsgLen > (nMaxPduLen - nPrefixLen))
					nPduMsgLen = nMaxPduLen - nPrefixLen;
				int nSpaceAvail = (nMaxPduLen - nPrefixLen) - nPduMsgLen;
				if (nSpaceAvail > 16 && // arbitrary number to decide whether there is enough left to be worth reading PR_BODY
					(pProps[4].ulPropTag != PT_ERROR) &&
					(pProps[4].Value.ul & (MSGSTATUS_HAS_PR_BODY | MSGSTATUS_HAS_PR_CE_MIME_TEXT))) 
				{
					ULONG ulProp = PR_CE_MIME_TEXT;
					IStream *	iBody;
					if (pProps[4].Value.ul & MSGSTATUS_HAS_PR_BODY)
						ulProp = PR_BODY;
					hr = pMessage->OpenProperty(ulProp, &IID_IStream, 0, 0, reinterpret_cast <IUnknown **>(&iBody));
					if ((hr == S_OK) && (iBody != NULL)) // iBody should always be valid if hr == S_OK
					{
						STATSTG stg = {0};
						memset(&stg, 0, sizeof(STATSTG));
						hr = iBody->Stat(&stg, STATFLAG_NONAME);
						if ((hr == S_OK))
						{
							char szBody[128];
							TCHAR bRaw[128];
							ULONG ulBytesRead;
							DWORD dwLen = stg.cbSize.LowPart;
							BYTE * pMsgBody = (BYTE *) LocalAlloc(LPTR, dwLen+2);
							if (pMsgBody == NULL)
							{
								pMsgBody = (BYTE *) &bRaw;
								dwLen = 128 * sizeof(TCHAR);
							}
							hr = iBody->Read(pMsgBody, dwLen, &ulBytesRead);
#ifdef DEBUG
							sprintf(szLog, "Read returned %8.8X, len = %i, read = %i", hr, dwLen, ulBytesRead);
							LogString(szLog, DEBUG_MESSAGES);
							// LogString((CHAR *) pMsgBody, DEBUG_MESSAGES);
#endif
							if (hr != S_OK)
							{
								dwLen=0;
							}
							else if (ulBytesRead < dwLen)
								dwLen=ulBytesRead;
							if (dwLen)
							{
								if (pProps[4].Value.ul & MSGSTATUS_HAS_PR_BODY)
								{
									*(pMsgBody + (nSpaceAvail-2)) = 0;
									// make sure both bytes of TCHAR are 0
									*(pMsgBody + (nSpaceAvail-1)) = 0;
									WideCharToMultiByte(CP_OEMCP, 0, (TCHAR *) pMsgBody, -1, szTemp1, sizeof(szTemp1), NULL, NULL);
									sprintf(szBody, ":%s", szTemp1);
								}
								else
								{
									szBody[0] = 0;
									char * pBody = strstr((CHAR *) pMsgBody, "\r\n\r\n");
									if (pBody != NULL)
									{
#ifdef DEBUG
										LogString(pBody, DEBUG_MESSAGES);
#endif
										TCHAR * szUniBody = (TCHAR *) LocalAlloc(LPTR, (nSpaceAvail+1) * sizeof(TCHAR));
										if (szUniBody != NULL)
										{
											pBody += 4;
											*(pBody + (nSpaceAvail-1)) = 0;
											*(pBody + (nSpaceAvail-2)) = 0;
											int nLen = MultiByteToWideChar(CP_UTF8, 0, pBody, -1, szUniBody, nSpaceAvail);
											if (nLen)
											{
												int nLen2 = WideCharToMultiByte(CP_OEMCP, 0, szUniBody, nLen, szTemp1, sizeof(szTemp1), NULL, NULL);
												szTemp1[nSpaceAvail] = 0;
												// *(pBody + (nSpaceAvail-2)) = 0;
#ifdef DEBUG
												sprintf(szLog, "MB2WC=%i, WC2MB=%i, %s", nLen, nLen2, szTemp1);
												LogString(szLog, DEBUG_MESSAGES);
#endif
												sprintf(szBody, ":%s", szTemp1);
											}
#ifdef DEBUG
											else
											{
												sprintf(szLog, "MB2WC Error %i", GetLastError());
												LogString(szLog, DEBUG_MESSAGES);
												// ERROR_INSUFFICIENT_BUFFER
											}
#endif
											LocalFree(szUniBody);
										}
									}
								}
								if (pMsgBody != (BYTE *) &bRaw)
									LocalFree(pMsgBody);
								strcat(szRawMsg, szBody);
								nPduMsgLen = strlen(szRawMsg);
							}
						}
#ifdef DEBUG
						else
						{
							sprintf(szLog, "Stat returned %8.8X", hr);
							LogString(szLog, DEBUG_MESSAGES);
						}
#endif
						iBody->Release();
					}
#ifdef DEBUG
					else
					{
						/*
						if (hr == MAPI_E_NO_SUPPORT)
						{
						}
						*/
						sprintf(szLog, "OpenProperty returned %8.8X", hr);
						LogString(szLog, DEBUG_MESSAGES);
					}
#endif
				}
#ifdef DEBUG
				else
				{
					sprintf(szLog, "Flags = %8.8X", pProps[4].Value.ul);
					LogString(szLog, DEBUG_MESSAGES);
				}
				LogString(szRawTime, DEBUG_MESSAGES);
				sprintf(szLog, "%s - len=%i", szRawMsg, nPduMsgLen);
				LogString(szLog, DEBUG_MESSAGES);
#endif

				int nMsgCoding = MSG_CODING_8BIT;
#ifdef TEST_7BIT
				nMsgCoding = MSG_CODING_7BIT; // 7-bit coding
#endif
				if (nMsgCoding == MSG_CODING_8BIT)
				{
					for (int i=0; i < nPduMsgLen; i++)
					{
						char szTemp[4];
						if (((unsigned char) (szRawMsg[i])) > 0x7F) //TODO: determine what range is valid for SMS
							szRawMsg[i] = 0x2E;
						sprintf(szTemp, "%2.2X", szRawMsg[i]);
						szPduMsg[i*2]= szTemp[0];
						szPduMsg[(i*2)+1] = szTemp[1];
					}
					szPduMsg[nPduMsgLen*2] = 0;
				}
				else
				{
					// TODO: May need to null terminate resulting string
					unsigned char * psz7BitText = NULL;
					int n7BitLen = 0;
					n7BitLen = ascii_to_pdu(szRawMsg, &psz7BitText);
					if (n7BitLen > 0)
					{
						strcpy(szRawMsg, (char *) psz7BitText);
						LocalFree(psz7BitText);
					}
				}
				// LogString(szPduMsg, DEBUG_MESSAGES);

				szPduTime[0] = szRawTime[3];// Year
				szPduTime[1] = szRawTime[2];
				szPduTime[2] = szRawTime[5];// Month
				szPduTime[3] = szRawTime[4];
				szPduTime[4] = szRawTime[7];// Day
				szPduTime[5] = szRawTime[6];
				szPduTime[6] = szRawTime[9];// Hour
				szPduTime[7] = szRawTime[8];
				szPduTime[8] = szRawTime[11]; // Min
				szPduTime[9] = szRawTime[10];
				szPduTime[10] = szRawTime[13];// Sec
				szPduTime[11] = szRawTime[12];
				szPduTime[12] = 0;

				// LogString(szPduTime, DEBUG_MESSAGES);
				sprintf(szMessage, "00040481000000%2.2X%s2B%2.2X%s", nMsgCoding, szPduTime, nPduMsgLen, szPduMsg);
				// LogString(szMessage, DEBUG_MESSAGES);
				// reuse szRawMsg buffer
				if ((pProps[3].ulPropTag != PT_ERROR) && !(pProps[3].Value.ul & MSGFLAG_READ))
					nMsgRead = 0;
				sprintf(szRawMsg, "+CMGR: %i,,%i", nMsgRead, (strlen(szMessage)/2)-1); 
				// LogString(szRawMsg, DEBUG_MESSAGES);
				AddLineToResponse(szRawMsg, FALSE);
				AddLineToResponse(szMessage, FALSE);
				bRet = TRUE;
				MAPIFreeBuffer(pProps);
			} // SUCCEEDED GetProps
			RELEASE_OBJ(pMessage);
		} // SUCCEEDED OpenEntry
	}
	LeaveCriticalSection(&csMsgList);
	EnterCriticalSection(&csStatus);
	bProcessingNewMail = FALSE;
	LeaveCriticalSection(&csStatus);
	return bRet;
}

BOOL DeleteMapiMessage(int nIndex)
{
	BOOL bRet = FALSE;
	LPMSGMAP pMsg = NULL;
	ENTRYLIST DeleteList;
	SBinary bin;
/*
	if (!pStores[g_dwMSSelected].ReadFirstMsg)
		nIndex--;
*/
	// now find the message
	EnterCriticalSection(&csMsgList);
	pMsg = FindMessage(nIndex);
	if (pMsg != NULL)
	{
		ULONG				ulInboxIDn	=	0;
		LPENTRYID			pInboxID	=	NULL;
		HRESULT				hr = S_OK;
		LPMAPIFOLDER        pfldrInbox  =   NULL;
		LPMAPIFOLDER        pfldrTrash  =   NULL;

		SizedSPropTagArray(1,rgTags) = {1, {PR_IPM_WASTEBASKET_ENTRYID}};
		ULONG ulPropCount = 0;
		LPSPropValue pProps = NULL;

#ifdef DEBUG
		CHAR szLog[1024];
#endif
		bin.cb = pMsg->IDLen;
		bin.lpb = (UCHAR *) pMsg->EntryID;
		DeleteList.cValues = 1;
		DeleteList.lpbin = &bin;
		hr = pStores[g_dwMSSelected].pStore->GetReceiveFolder(NULL, 0, &ulInboxIDn, &pInboxID, NULL);
#ifdef DEBUG_MAPI
		sprintf(szLog, "GetReceiveFolder returned 0x%8.8X", hr);
		LogString(szLog, DEBUG_MESSAGES);
#endif
		if (hr == S_OK)
		{
			hr = pStores[g_dwMSSelected].pStore->GetProps((LPSPropTagArray) &rgTags, MAPI_UNICODE, &ulPropCount, &pProps);
#ifdef DEBUG
			sprintf(szLog, "GetProps returned 0x%8.8X", hr);
			LogString(szLog, DEBUG_MESSAGES);
#endif
			if (HR_SUCCEEDED(hr))
			{
				if (LOWORD(pProps[0].ulPropTag) == PT_BINARY)
				{
					hr = pStores[g_dwMSSelected].pStore->OpenEntry(pProps[0].Value.bin.cb, (LPENTRYID) pProps[0].Value.bin.lpb, NULL, 0, NULL,
							reinterpret_cast <IUnknown **>(&pfldrTrash));
#ifdef DEBUG_MAPI
					sprintf(szLog, "OpenEntry (trash) returned 0x%8.8X", hr);
					LogString(szLog, DEBUG_MESSAGES);
#endif
					if (hr == S_OK)
					{
						hr = pStores[g_dwMSSelected].pStore->OpenEntry(ulInboxIDn, pInboxID, NULL, 0, NULL, 
							reinterpret_cast <IUnknown **>(&pfldrInbox));
#ifdef DEBUG
						sprintf(szLog, "OpenEntry (inbox) returned 0x%8.8X", hr);
						LogString(szLog, DEBUG_MESSAGES);
#endif
						if (hr == S_OK)
						{
							hr = pfldrInbox->CopyMessages(&DeleteList, NULL, pfldrTrash, 0, NULL, MESSAGE_MOVE);
							if (HR_SUCCEEDED(hr))
							{
								DeleteMessage(nIndex);
								bRet = TRUE;
							}
							RELEASE_OBJ(pfldrInbox);
						}
						RELEASE_OBJ(pfldrTrash);
					}
				}
				MAPIFreeBuffer(pProps);
			}
			MAPIFreeBuffer(pInboxID);
		}
	}
	LeaveCriticalSection(&csMsgList);
	return bRet;
}

/*
CAdviseSink::CAdviseSink(DWORD dwMsgStore)
{
	m_dwMsgStore = dwMsgStore;
	m_cRef = 1;
};	

CAdviseSink::~CAdviseSink()
{
};


STDMETHODIMP CAdviseSink::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
	*ppvObj = 0;
#ifdef DEBUG
	char szLog[512];
	sprintf(szLog, "QueryInterface for REFIID %8.8X-%4.4X-%4.4X-%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X",
		riid.Data1, riid.Data2, riid.Data3, riid.Data4[0], riid.Data4[1], riid.Data4[2], riid.Data4[3],
		riid.Data4[4], riid.Data4[5], riid.Data4[6], riid.Data4[7]);
	LogString(szLog, DEBUG_MESSAGES);
#endif
	if (riid == IID_IMAPIAdviseSink || riid == IID_IUnknown)
	{
		*ppvObj = (LPVOID)this;
		AddRef();
		return S_OK;
	}
	return E_NOINTERFACE;
};

STDMETHODIMP_(ULONG) CAdviseSink::AddRef() 
{
	LONG lCount = InterlockedIncrement(&m_cRef);
	return lCount; 
};

STDMETHODIMP_(ULONG) CAdviseSink::Release() 
{
	LONG lCount = InterlockedDecrement(&m_cRef);
	if (!lCount)  delete this; 
	return lCount;
};

STDMETHODIMP_(ULONG) CAdviseSink::OnNotify(ULONG cNotify, LPNOTIFICATION lpNotifications)
{
	HRESULT	hRes = S_OK;
#ifdef DEBUG
		char szLog[64];
		sprintf(szLog, "OnNotify called with %i notifications", cNotify);
		LogString(szLog, DEBUG_MESSAGES);
#endif
	
	for (ULONG i=0; i < cNotify; i++)
	{
#ifdef DEBUG
		sprintf(szLog, "Notification: event type %i, obj type %i", lpNotifications[i].ulEventType, lpNotifications[i].info.obj.ulObjType);
		LogString(szLog, DEBUG_MESSAGES);
#endif
		hRes = S_OK;
		if (lpNotifications[i].ulEventType == fnevObjectCreated)
		{
			if(lpNotifications[i].info.obj.ulObjType == MAPI_MESSAGE)
			{
				EnterCriticalSection(&csMsgList);
				DWORD dwTempMsgStore = g_dwMSSelected;
				WORD wIndex = 0;
				g_dwMSSelected = m_dwMsgStore;
				wIndex = AddMsg(lpNotifications[i].info.obj.cbEntryID, lpNotifications[i].info.obj.lpEntryID);
				if (wIndex != 0)
				{
					char szResponse[32];
					sprintf(szResponse, "+CMTI: \"%s\",%i", MSLIST[m_dwMsgStore], wIndex);
					PostNewMail(szResponse);
				}
				g_dwMSSelected = dwTempMsgStore;
				LeaveCriticalSection(&csMsgList);
			}
		}
	}
	return 0;
}
*/

// This is a polling-basd work around to MAPI Advise not working
void CheckForNewMessages()
{
	if (pSession != NULL)
	{
		LPMAPIFOLDER        pfldrInbox  =   NULL;

		HRESULT             hr = S_OK;
		IMAPITable		*   pContentsTable = NULL;
		SRowSet         *   psrs        =   NULL;
		ULONG               rgTags[]    =   { 1, PR_RESOURCE_FLAGS };
		ULONG               cValues     =   0;
		LPSRowSet			pRows		=	NULL;

		ULONG				ulInboxIDn	=	0;
		LPENTRYID			pInboxID	=	NULL;
	#ifdef DEBUG
		CHAR szLog[64];
	#endif

		SizedSPropTagArray(3, sContents) = { 3, {PR_ENTRYID, PR_MESSAGE_DELIVERY_TIME, PR_MESSAGE_FLAGS} };

		for (int i = 0; i <= 1; i++)
		{
			if (pStores[i].Notify && (pStores[i].pStore != NULL))
			{
				// Now get the Inbox folder
				hr = pStores[i].pStore->GetReceiveFolder(NULL, 0, &ulInboxIDn, &pInboxID, NULL);
#ifdef DEBUG_MAPI
				sprintf(szLog, "GetReceiveFolder returned 0x%8.8X", hr);
				LogString(szLog, DEBUG_MESSAGES);
#endif
				if (hr == S_OK)
				{
					hr = pStores[i].pStore->OpenEntry(ulInboxIDn, pInboxID, NULL, 0, NULL, 
						reinterpret_cast <IUnknown **>(&pfldrInbox));
#ifdef DEBUG_MAPI
					sprintf(szLog, "OpenEntry returned 0x%8.8X", hr);
					LogString(szLog, DEBUG_MESSAGES);
#endif
					if (hr == S_OK)
					{
						if (pfldrInbox->GetContentsTable(0, &pContentsTable) == S_OK)
						{
							ULONG ulNumMsgs = 0;
#ifdef DEBUG_MAPI
							sprintf(szLog, "GetContentsTable succeeded");
							LogString(szLog, DEBUG_MESSAGES);
#endif
							if (pContentsTable->GetRowCount(0,&ulNumMsgs) == S_OK)
							{
#ifdef DEBUG
								sprintf(szLog, "GetRowCount found %i rows", ulNumMsgs);
								LogString(szLog, DEBUG_MESSAGES);
#endif
								if (pContentsTable->SetColumns((LPSPropTagArray)&sContents, NULL) == S_OK)
								{
									WORD nIndex = 1;
									LPSRowSet	pMsgRows	=	NULL;
#ifdef DEBUG_MAPI
									sprintf(szLog, "SetColumns succeeded");
									LogString(szLog, DEBUG_MESSAGES);
#endif
									SizedSSortOrderSet(1, pSort)={ 1,0,0, {PR_MESSAGE_DELIVERY_TIME, TABLE_SORT_DESCEND} };
									pContentsTable->SortTable((LPSSortOrderSet)&pSort, 0);
									// loop through QueryRows until we have found Max Messages for this store
									EnterCriticalSection(&csMsgList);
									while (pContentsTable->QueryRows(10, 0, &pMsgRows) == S_OK)
									{
										if (pMsgRows->cRows <= 0)
											break;
#ifdef DEBUG
										sprintf(szLog, "QueryRows returned %i rows", pMsgRows->cRows);
										LogString(szLog, DEBUG_MESSAGES);
#endif
										// ulNumMsgs = pMsgRows->cRows;
										for (UINT j = 0; j < pMsgRows->cRows; j++)
										{
											if (nIndex++ < g_dwMsgMax[i] && ((g_bAllMsgs[i]) || (!(pMsgRows->aRow[j].lpProps[2].Value.ul & MSGFLAG_READ))))
											{
												//TODO: Pass msgstore ID to AddMsg
												DWORD dwTempMsgStore = g_dwMSSelected;
												g_dwMSSelected = i;
												WORD wMsgIndex = AddMsg(pMsgRows->aRow[j].lpProps[0].Value.bin.cb, 
													(LPENTRYID)pMsgRows->aRow[j].lpProps[0].Value.bin.lpb);
												g_dwMSSelected = dwTempMsgStore;
												if (wMsgIndex >= 1)
												{
													char szResponse[32];
													/*
													if (!pStores[i].ReadFirstMsg)
														wMsgIndex++;
													*/
													sprintf(szResponse, "+CMTI: \"%s\",%i", MSLIST[i], wMsgIndex);
#ifdef DEBUG
//													LogString(szResponse, DEBUG_MESSAGES);
#endif
													PostNewMail(szResponse);
												}
												else // since messages are sorted newest first, when we find the first one already known we can stop looking
												{
													nIndex = (WORD) g_dwMsgMax[i];
													break;
												}
											}
										}
										FreeProws(pMsgRows);
										if (nIndex >= g_dwMsgMax[i])
											break;
									} // SUCCEEDED QueryRows
									LeaveCriticalSection(&csMsgList);
								} // SUCCEEDED SetColumns
							} // SUCCEEDED GetRowCount
						} // SUCCEEDED GetContentsTable
					} // SUCCEEDED OpenEntry
					// now set up the new message notification registration
					MAPIFreeBuffer(pInboxID);
				} // SUCCEEDED GetReceiveFolder
			} // SUCCEEDED OpenMsgStore
		}
	}
}
